#!/bin/bash
# Shell functions for parsing the Linux kernel version and for downloading
# from kernel.org.

# shellcheck source=./rhel-rpm-functions
source "$(dirname "$0")/../scripts/rhel-rpm-functions" || return $?

kernel_mirror="http://cdn.kernel.org/pub/linux/kernel"
kernel_downloads="$HOME/software/downloads"
kernel_tree="$HOME/software/linux-kernel"

# Whether or not kernel version $1 is lower than or equal kernel version $2.
function kernel_version_le {
    awk -v "v1=$1" -v "v2=$2" 'BEGIN { n1 = split(v1, v1a, "."); n2 = split(v2, v2a, "."); for (i=1;;i++) { e1 = i <= n1 ? v1a[i] : 0; e2 = i <= n2 ? v2a[i] : 0; if (e1 < e2 || i > n1 && i > n2) exit 0; if (e1 > e2) exit 1; }}'
}

function kernel_version_lt {
    [ "$1" != "$2" ] && kernel_version_le "$1" "$2"
}

# Kernel version number.
function kernel_version {
  if [ "${1#2.}" != "$1" ]; then
    echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\.[0-9]*\).*$/\1/p'
  else
    echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\).*$/\1/p'
  fi
}

# Last component of the kernel version, or the empty string if $1 does
# not contain a patchlevel.
function patchlevel {
  if [ "${1#2.}" != "$1" ]; then
    echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\.[0-9]*\)[.-]\(.*\)$/\2/p'
  else
    echo "$1" | sed -n 's/^\([0-9]*\.[0-9]*\)[.-]\(.*\)$/\2/p'
  fi
}

# Download the file from URL $1 and save it in the current directory.
function download_file {
  if [ ! -e "$(basename "$1")" ]; then
    if [ "${quiet_download}" = "false" ]; then
      { wget -q -nc -O- "$1" 2>/dev/null | grep -q .; } \
        && echo "Downloading $1 ..."
    fi
    wget -q -nc "$1"
  fi
  [ -e "$(basename "$1")" ]
}

# Make sure the kernel tarball and patch file are present in directory
# ${kernel_downloads}. Download any missing files from ${kernel_mirror}.
function download_kernel {
  local kver="$(kernel_version "$1")"
  local plevel="$(patchlevel "$1")"
  local series="$1"

  case "${series:0:2}" in
      [12].*) series="${series:0:3}";;
      *)      series="${series/.*/}.x";;
  esac
  mkdir -p "${kernel_downloads}" || return $?
  test -w "${kernel_downloads}" || return $?
  (
    cd "${kernel_downloads}" || return $?
    if [ "$plevel" = "" ] || [ "$plevel" = "0" ] ||
      download_file "${kernel_mirror}/v$series/patch-$1.xz"
    then
      download_file "${kernel_mirror}/v$series/linux-${kver}.tar.xz" ||
	return $?
    else
      download_file "${kernel_mirror}/v$series/linux-$1.tar.xz" ||
	return $?
    fi
  )
}

function extract_kernel_archive {
  local kver="$(kernel_version "$1")"
  local plevel="$(patchlevel "$1")"
  local series="$1"

  if [ -e "${kernel_downloads}/linux-$1.tar.xz" ]; then
    xz -cd "${kernel_downloads}/linux-$1.tar.xz" | tar xf -
  elif [ -e "${kernel_downloads}/linux-$kver.tar.xz" ]; then
    xz -cd "${kernel_downloads}/linux-$kver.tar.xz" | tar xf - &&
      mv "linux-$kver" "linux-$1"
  elif [ -e "${kernel_downloads}/linux-$1.tar.bz2" ]; then
    tar xjf "${kernel_downloads}/linux-$1.tar.bz2"
  elif [ -e "${kernel_downloads}/linux-$kver.tar.bz2" ]; then
    tar xjf "${kernel_downloads}/linux-$kver.tar.bz2" &&
      mv "linux-$kver" "linux-$1"
  else
    return 1
  fi
}

# Create a linux-$1 tree in the current directory, where $1 is a kernel
# version number with either three or four components.
function extract_kernel_tree {
  local kver="$(kernel_version "$1")"
  local plevel="$(patchlevel "$1")"
  local tmpdir=kernel-tree-tmp-$$

  rm -rf "linux-$1" "${tmpdir}"
  mkdir "${tmpdir}" || return $?
  (
    cd "${tmpdir}" || return $?
    if [ "$plevel" != "" ] && [ "$plevel" != "0" ] &&
       [ -e "${kernel_downloads}/patch-$1.xz" ]; then
      extract_kernel_archive "$kver" || return $?
      mv "linux-$kver" "linux-$1"
      ( cd "linux-$1" && xz -cd "${kernel_downloads}/patch-$1.xz" \
        | patch -p1 -f -s; ) \
        || return $?
    else
	extract_kernel_archive "$1" ||
	{ extract_kernel_archive "$kver" && mv "linux-$kver" "linux-$1"; } ||
	return $?
    fi
    mv "linux-$1" .. || return $?
    cd "../linux-$1" || return $?
  ) || return $?
  rmdir "${tmpdir}"
}

# Patch a kernel tree where $1 is the kernel version.
function patch_kernel {
    case "$1" in
	*^*)
            # RHEL / CentOS or UEK.
            ;;
	*)
	    # See also commit f153b82121b0 ("Sanitize gcc version header
	    # includes") # v2.6.29. See also commit 71458cfc782e ("kernel: add
	    # support for gcc 5") # v3.18. See also commit cb984d101b30
	    # ("compiler-gcc: integrate the various compiler-gcc[345].h
	    # files") # v4.2.
	    if kernel_version_le 2.6.29 "$1" && kernel_version_lt "$1" 4.2; then
		# Tell the kernel that we are using gcc 4.6 since older kernel
		# versions do not support recent gcc versions. See also commit
		# 9c695203a7dd ("compiler-gcc.h: gcc-4.5 needs noclone and
		# noinline on __naked functions") # v2.6.35.
		if kernel_version_le 2.6.34.2 "$1"; then
		    patch -f -s -p1 <<'EOF'
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index 02ae99e8e6d3..47e12c19c965 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -103,7 +103,7 @@
 #define __gcc_header(x) #x
 #define _gcc_header(x) __gcc_header(linux/compiler-gcc##x.h)
 #define gcc_header(x) _gcc_header(x)
-#include gcc_header(__GNUC__)
+#include "linux/compiler-gcc4.h"
 
 #if !defined(__noclone)
 #define __noclone	/* not needed */
EOF
		else
		    patch -f -s -p1 <<'EOF'
diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
index a3ed7cb8ca34..c5a6b8b52db4 100644
--- a/include/linux/compiler-gcc.h
+++ b/include/linux/compiler-gcc.h
@@ -83,4 +83,4 @@
 #define __gcc_header(x) #x
 #define _gcc_header(x) __gcc_header(linux/compiler-gcc##x.h)
 #define gcc_header(x) _gcc_header(x)
-#include gcc_header(__GNUC__)
+#include "linux/compiler-gcc4.h"
EOF
		fi
		# See also commit 733ed6e43756 ("compiler-gcc{3,4}.h: Use
		# GCC_VERSION macro") # v3.9.
		if kernel_version_le 3.9 "$1"; then
		    patch -f -s -p1 <<'EOF'
diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h
index 769e19864632..2ec6c7a11502 100644
--- a/include/linux/compiler-gcc4.h
+++ b/include/linux/compiler-gcc4.h
@@ -2,13 +2,6 @@
 #error "Please don't include <linux/compiler-gcc4.h> directly, include <linux/compiler.h> instead."
 #endif
 
-/* GCC 4.1.[01] miscompiles __weak */
-#ifdef __KERNEL__
-# if GCC_VERSION >= 40100 &&  GCC_VERSION <= 40101
-#  error Your version of gcc miscompiles the __weak directive
-# endif
-#endif
-
 #define __used			__attribute__((__used__))
 #define __must_check 		__attribute__((warn_unused_result))
 #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
EOF
		else
		    patch -f -s -p1 <<'EOF'
diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h
index 412bc6c2b023..901ca31be7f8 100644
--- a/include/linux/compiler-gcc4.h
+++ b/include/linux/compiler-gcc4.h
@@ -2,13 +2,6 @@
 #error "Please don't include <linux/compiler-gcc4.h> directly, include <linux/compiler.h> instead."
 #endif
 
-/* GCC 4.1.[01] miscompiles __weak */
-#ifdef __KERNEL__
-# if __GNUC_MINOR__ == 1 && __GNUC_PATCHLEVEL__ <= 1
-#  error Your version of gcc miscompiles the __weak directive
-# endif
-#endif
-
 #define __used			__attribute__((__used__))
 #define __must_check 		__attribute__((warn_unused_result))
 #define __compiler_offsetof(a,b) __builtin_offsetof(a,b)
EOF
		fi
	    fi
	    ;;
    esac

    if [ "${1#2.6.31}" != "$1" ]; then
      patch -f -s -p1 <<'EOF'
Checking a 2.6.31.1 kernel configured with allyesconfig/allmodconfig
with sparse (make C=2) triggers a sparse warning on code that uses the
kmemcheck_annotate_bitfield() macro. An example of such a warning:

include/net/inet_sock.h:208:17: warning: do-while statement is not a compound statement

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Cc: Vegard Nossum <vegardno@ifi.uio.no>
Cc: Andrew Morton <akpm@linux-foundation.org>

---
See also http://lkml.org/lkml/2009/9/26/51

--- linux-2.6.31.1/include/linux/kmemcheck-orig.h	2009-09-26 13:53:44.000000000 +0200
+++ linux-2.6.31.1/include/linux/kmemcheck.h	2009-09-26 13:53:56.000000000 +0200
@@ -137,13 +137,13 @@ static inline void kmemcheck_mark_initia
 	int name##_end[0];
 
 #define kmemcheck_annotate_bitfield(ptr, name)				\
-	do if (ptr) {							\
+	do { if (ptr) {							\
 		int _n = (long) &((ptr)->name##_end)			\
 			- (long) &((ptr)->name##_begin);		\
 		BUILD_BUG_ON(_n < 0);					\
 									\
 		kmemcheck_mark_initialized(&((ptr)->name##_begin), _n);	\
-	} while (0)
+	} } while (0)
 
 #define kmemcheck_annotate_variable(var)				\
 	do {								\
EOF
    fi
    if [ "${1#2.6.32}" != "$1" ] || [ "${1#2.6.33}" != "$1" ]
    then
      patch -f -s -p1 <<'EOF'
Get rid of sparse errors on sk_buff.protocol.

--- linux/include/linux/skbuff-orig.h	2010-12-07 13:40:51.000000000 -0500
+++ linux/include/linux/skbuff.h	2010-12-07 13:41:05.000000000 -0500
@@ -349,8 +349,8 @@ struct sk_buff {
 				ipvs_property:1,
 				peeked:1,
 				nf_trace:1;
-	__be16			protocol:16;
 	kmemcheck_bitfield_end(flags1);
+	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
EOF
    fi

    if [ "${1#3.13}" != "$1" ]; then
      if [ "$1" = "3.13" ] || [ "${1#3.13.}" -lt 6 ]; then
        patch -f -s -p1 <<'EOF'
From 7b4ec8dd7d4ac467e9eee4d49f2c9574d773efbb Mon Sep 17 00:00:00 2001
From: Johannes Berg <johannes.berg@intel.com>
Date: Thu, 16 Jan 2014 10:18:48 +1030
Subject: [PATCH] export: declare ksymtab symbols

sparse complains about any __ksymtab symbols with the following:

 warning: symbol '__ksymtab_...' was not declared. Should it be static?

due to Andi's patch making it non-static.

Mollify sparse by declaring the symbol extern, otherwise we get
drowned in sparse warnings for anything that uses EXPORT_SYMBOL
in the sources, making it easy to miss real warnings.

Fixes: e0f244c63fc9 ("asmlinkage, module: Make ksymtab [...] __visible")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 include/linux/export.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/export.h b/include/linux/export.h
index 3f2793d..96e45ea 100644
--- a/include/linux/export.h
+++ b/include/linux/export.h
@@ -59,6 +59,7 @@ extern struct module __this_module;
 	static const char __kstrtab_##sym[]			\
 	__attribute__((section("__ksymtab_strings"), aligned(1))) \
 	= VMLINUX_SYMBOL_STR(sym);				\
+	extern const struct kernel_symbol __ksymtab_##sym;	\
 	__visible const struct kernel_symbol __ksymtab_##sym	\
 	__used							\
 	__attribute__((section("___ksymtab" sec "+" #sym), unused))	\
EOF
      fi
    fi

    if [ "${1#4.15}" != "$1" ]; then
        patch -f -s -p1 <<'EOF'
From ad343a98e74e85aa91d844310e797f96fee6983b Mon Sep 17 00:00:00 2001
From: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Date: Tue, 6 Feb 2018 15:37:52 -0800
Subject: [PATCH] tools/lib/subcmd/pager.c: do not alias select() params

Use a separate fd set for select()-s exception fds param to fix the
following gcc warning:

  pager.c:36:12: error: passing argument 2 to restrict-qualified parameter aliases with argument 4 [-Werror=restrict]
    select(1, &in, NULL, &in, NULL);
              ^~~        ~~~

Link: http://lkml.kernel.org/r/20180101105626.7168-1-sergey.senozhatsky@gmail.com
Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
---
 tools/lib/subcmd/pager.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/tools/lib/subcmd/pager.c b/tools/lib/subcmd/pager.c
index 5ba754d17952..9997a8805a82 100644
--- a/tools/lib/subcmd/pager.c
+++ b/tools/lib/subcmd/pager.c
@@ -30,10 +30,13 @@ static void pager_preexec(void)
 	 * have real input
 	 */
 	fd_set in;
+	fd_set exception;
 
 	FD_ZERO(&in);
+	FD_ZERO(&exception);
 	FD_SET(0, &in);
-	select(1, &in, NULL, &in, NULL);
+	FD_SET(0, &exception);
+	select(1, &in, NULL, &exception, NULL);
 
 	setenv("LESS", "FRSX", 0);
 }
EOF
        patch -f -s -p1 <<'EOF'
From 854e55ad289ef8888e7991f0ada85d5846f5afb9 Mon Sep 17 00:00:00 2001
From: Josh Poimboeuf <jpoimboe@redhat.com>
Date: Thu, 15 Mar 2018 22:11:54 -0500
Subject: [PATCH] objtool, perf: Fix GCC 8 -Wrestrict error
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Starting with recent GCC 8 builds, objtool and perf fail to build with
the following error:

  ../str_error_r.c: In function 'str_error_r':
  ../str_error_r.c:25:3: error: passing argument 1 to restrict-qualified parameter aliases with argument 5 [-Werror=restrict]
     snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, %p, %zd)=%d", errnum, buf, buflen, err);

The code seems harmless, but there's probably no benefit in printing the
'buf' pointer in this situation anyway, so just remove it to make GCC
happy.

Reported-by: Laura Abbott <labbott@redhat.com>
Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Tested-by: Laura Abbott <labbott@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/20180316031154.juk2uncs7baffctp@treble
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/lib/str_error_r.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/lib/str_error_r.c b/tools/lib/str_error_r.c
index d6d65537b0d9..6aad8308a0ac 100644
--- a/tools/lib/str_error_r.c
+++ b/tools/lib/str_error_r.c
@@ -22,6 +22,6 @@ char *str_error_r(int errnum, char *buf, size_t buflen)
 {
 	int err = strerror_r(errnum, buf, buflen);
 	if (err)
-		snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, %p, %zd)=%d", errnum, buf, buflen, err);
+		snprintf(buf, buflen, "INTERNAL ERROR: strerror_r(%d, [buf], %zd)=%d", errnum, buflen, err);
 	return buf;
 }
EOF
    fi

    if kernel_version_le 2.6.36 "$1" && kernel_version_lt "$1" 4.8; then
	patch -f -s -p1 <<'EOF'
From c6a385539175ebc603da53aafb7753d39089f32e Mon Sep 17 00:00:00 2001
From: Borislav Petkov <bp@suse.de>
Date: Mon, 14 Nov 2016 19:41:31 +0100
Subject: [PATCH] kbuild: Steal gcc's pie from the very beginning

So Sebastian turned off the PIE for kernel builds but that was too late
- Kbuild.include already uses KBUILD_CFLAGS and trying to disable gcc
options with, say cc-disable-warning, fails:

  gcc -D__KERNEL__ -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs
  ...
  -Wno-sign-compare -fno-asynchronous-unwind-tables -Wframe-address -c -x c /dev/null -o .31392.tmp
  /dev/null:1:0: error: code model kernel does not support PIC mode

because that returns an error and we can't disable the warning. For
example in this case:

KBUILD_CFLAGS   += $(call cc-disable-warning,frame-address,)

which leads to gcc issuing all those warnings again.

So let's turn off PIE/PIC at the earliest possible moment, when we
declare KBUILD_CFLAGS so that cc-disable-warning picks it up too.

Also, we need the $(call cc-option ...) because -fno-PIE is supported
since gcc v3.4 and our lowest supported gcc version is 3.2 right now.

Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: stable@vger.kernel.org
Cc: Ben Hutchings <ben@decadent.org.uk>
Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Michal Marek <mmarek@suse.com>
[ bvanassche: moved -fno-PIE to start of KBUILD_CFLAGS ]
---
 Makefile | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/Makefile b/Makefile
index 0ed6ce300543..c324b43712f0 100644
--- a/Makefile
+++ b/Makefile
@@ -378,7 +378,8 @@ LINUXINCLUDE    := \
 
 KBUILD_CPPFLAGS := -D__KERNEL__
 
-KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
+KBUILD_CFLAGS   := $(call cc-option,-fno-PIE) \
+		   -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
 		   -fno-strict-aliasing -fno-common \
 		   -Werror-implicit-function-declaration \
 		   -Wno-format-security \
@@ -387,7 +388,7 @@ KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
 
 KBUILD_AFLAGS_KERNEL :=
 KBUILD_CFLAGS_KERNEL :=
-KBUILD_AFLAGS   := -D__ASSEMBLY__
+KBUILD_AFLAGS   := -D__ASSEMBLY__ $(call cc-option,-fno-PIE)
 KBUILD_AFLAGS_MODULE  := -DMODULE
 KBUILD_CFLAGS_MODULE  := -DMODULE
 KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds
EOF
    else
	case "$1" in
	    # CentOS 6.x.
	    2.6.32-*)
		patch -f -s -p1 <<'EOF'
--- linux-2.6.32-754.29.1.el6/Makefile.orig	2020-05-13 14:09:18.448503420 -0700
+++ linux-2.6.32-754.29.1.el6/Makefile	2020-05-13 14:11:24.265441790 -0700
@@ -355,7 +355,8 @@
 
 KBUILD_CPPFLAGS := -D__KERNEL__
 
-KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
+KBUILD_CFLAGS   := $(call cc-option,-fno-PIE) \
+		   -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
 		   -fno-strict-aliasing -fno-common \
 		   -Werror-implicit-function-declaration \
 		   -Wno-format-security \
@@ -380,7 +381,7 @@
 endif ##($(KBUILD_EXTMOD),)
 endif #(,$(filter $(ARCH), i386 x86_64))
 
-KBUILD_AFLAGS   := -D__ASSEMBLY__
+KBUILD_AFLAGS   := -D__ASSEMBLY__ $(call cc-option,-fno-PIE)
 
 # Read KERNELRELEASE from include/config/kernel.release (if it exists)
 KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
EOF
		;;
	    2.6.3[1-5]*)
		patch -f -s -p1 <<'EOF'
diff --git a/Makefile b/Makefile
index 141da26fda4b..343ec388ae2e 100644
--- a/Makefile
+++ b/Makefile
@@ -349,12 +349,13 @@ LINUXINCLUDE    := -I$(srctree)/arch/$(hdr-arch)/include -Iinclude \
 
 KBUILD_CPPFLAGS := -D__KERNEL__
 
-KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
+KBUILD_CFLAGS   := $(call cc-option,-fno-PIE) \
+		   -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
 		   -fno-strict-aliasing -fno-common \
 		   -Werror-implicit-function-declaration \
 		   -Wno-format-security \
 		   -fno-delete-null-pointer-checks
-KBUILD_AFLAGS   := -D__ASSEMBLY__
+KBUILD_AFLAGS   := -D__ASSEMBLY__ $(call cc-option,-fno-PIE)
 
 # Read KERNELRELEASE from include/config/kernel.release (if it exists)
 KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
EOF
	    ;;
	 esac
    fi

    case "$1" in
	4.18.0-*) # CentOS 8.x
	    patch -f -s -p1 <<'EOF'
From a6e60d84989fa0e91db7f236eda40453b0e44afa Mon Sep 17 00:00:00 2001
From: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
Date: Sat, 19 Jan 2019 20:59:34 +0100
Subject: [PATCH] include/linux/module.h: copy __init/__exit attrs to
 init/cleanup_module

The upcoming GCC 9 release extends the -Wmissing-attributes warnings
(enabled by -Wall) to C and aliases: it warns when particular function
attributes are missing in the aliases but not in their target.

In particular, it triggers for all the init/cleanup_module
aliases in the kernel (defined by the module_init/exit macros),
ending up being very noisy.

These aliases point to the __init/__exit functions of a module,
which are defined as __cold (among other attributes). However,
the aliases themselves do not have the __cold attribute.

Since the compiler behaves differently when compiling a __cold
function as well as when compiling paths leading to calls
to __cold functions, the warning is trying to point out
the possibly-forgotten attribute in the alias.

In order to keep the warning enabled, we decided to silence
this case. Ideally, we would mark the aliases directly
as __init/__exit. However, there are currently around 132 modules
in the kernel which are missing __init/__exit in their init/cleanup
functions (either because they are missing, or for other reasons,
e.g. the functions being called from somewhere else); and
a section mismatch is a hard error.

A conservative alternative was to mark the aliases as __cold only.
However, since we would like to eventually enforce __init/__exit
to be always marked,  we chose to use the new __copy function
attribute (introduced by GCC 9 as well to deal with this).
With it, we copy the attributes used by the target functions
into the aliases. This way, functions that were not marked
as __init/__exit won't have their aliases marked either,
and therefore there won't be a section mismatch.

Note that the warning would go away marking either the extern
declaration, the definition, or both. However, we only mark
the definition of the alias, since we do not want callers
(which only see the declaration) to be compiled as if the function
was __cold (and therefore the paths leading to those calls
would be assumed to be unlikely).

Link: https://lore.kernel.org/lkml/20190123173707.GA16603@gmail.com/
Link: https://lore.kernel.org/lkml/20190206175627.GA20399@gmail.com/
Suggested-by: Martin Sebor <msebor@gcc.gnu.org>
Acked-by: Jessica Yu <jeyu@kernel.org>
Signed-off-by: Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
---
 include/linux/module.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index 8fa38d3e7538..f5bc4c046461 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -129,13 +129,13 @@ extern void cleanup_module(void);
 #define module_init(initfn)					\
 	static inline initcall_t __maybe_unused __inittest(void)		\
 	{ return initfn; }					\
-	int init_module(void) __attribute__((alias(#initfn)));
+	int init_module(void) __attribute__((__copy__(initfn))) __attribute__((alias(#initfn)));
 
 /* This is only required if you want to be unloadable. */
 #define module_exit(exitfn)					\
 	static inline exitcall_t __maybe_unused __exittest(void)		\
 	{ return exitfn; }					\
-	void cleanup_module(void) __attribute__((alias(#exitfn)));
+	void cleanup_module(void) __attribute__((__copy__(exitfn))) __attribute__((alias(#exitfn)));
 
 #endif
 
EOF
	;;
	2.*|3.*)
	    patch -f -s -p1 <<'EOF'
--- linux-3.10.0-1127.el7/include/linux/init.h.orig	2020-05-09 20:55:48.638956513 -0700
+++ linux-3.10.0-1127.el7/include/linux/init.h	2020-05-09 20:56:46.947612445 -0700
@@ -309,13 +309,15 @@
 #define module_init(initfn)					\
 	static inline initcall_t __inittest(void)		\
 	{ return initfn; }					\
-	int init_module(void) __attribute__((alias(#initfn)));
+	int init_module(void) __attribute__((__copy__(initfn)))	\
+	__attribute__((alias(#initfn)));
 
 /* This is only required if you want to be unloadable. */
 #define module_exit(exitfn)					\
 	static inline exitcall_t __exittest(void)		\
 	{ return exitfn; }					\
-	void cleanup_module(void) __attribute__((alias(#exitfn)));
+	void cleanup_module(void) __attribute__((__copy__(exitfn)))\
+	__attribute__((alias(#exitfn)));
 
 #define __setup_param(str, unique_id, fn)	/* nothing */
 #define __setup(str, func) 			/* nothing */
EOF
	    ;;
    esac

    if kernel_version_le 3.2 "$1" && kernel_version_lt "$1" 3.18; then
	patch -f -s -p1 <<'EOF'
From eeeda4cd06e828b331b15741a204ff9f5874d28d Mon Sep 17 00:00:00 2001
From: Ben Hutchings <ben@decadent.org.uk>
Date: Wed, 24 Sep 2014 13:30:12 +0100
Subject: [PATCH] x86/relocs: Make per_cpu_load_addr static

per_cpu_load_addr is only used for 64-bit relocations, but is
declared in both configurations of relocs.c - with different
types.  This has undefined behaviour in general.  GNU ld is
documented to use the larger size in this case, but other tools
may differ and some warn about this.

References: https://bugs.debian.org/748577
Reported-by: Michael Tautschnig <mt@debian.org>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Cc: 748577@bugs.debian.org
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Link: http://lkml.kernel.org/r/1411561812.3659.23.camel@decadent.org.uk
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 arch/x86/tools/relocs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c
index bbb1d2259ecf..a5efb21d5228 100644
--- a/arch/x86/tools/relocs.c
+++ b/arch/x86/tools/relocs.c
@@ -695,7 +695,7 @@ static void walk_relocs(int (*process)(struct section *sec, Elf_Rel *rel,
  *
  */
 static int per_cpu_shndx	= -1;
-Elf_Addr per_cpu_load_addr;
+static Elf_Addr per_cpu_load_addr;
 
 static void percpu_init(void)
 {
EOF
    fi

    # See also commit e33a814e772c ("scripts/dtc: Remove redundant YYLOC global
    # declaration") # v5.6~10^2.
    if kernel_version_le 2.6.38 "$1" && kernel_version_lt "$1" 5.6; then
	patch -p1 -f -s <<'EOF'
From e33a814e772cdc36436c8c188d8c42d019fda639 Mon Sep 17 00:00:00 2001
From: Dirk Mueller <dmueller@suse.com>
Date: Tue, 14 Jan 2020 18:53:41 +0100
Subject: [PATCH] scripts/dtc: Remove redundant YYLOC global declaration

gcc 10 will default to -fno-common, which causes this error at link
time:

  (.text+0x0): multiple definition of `yylloc'; dtc-lexer.lex.o (symbol from plugin):(.text+0x0): first defined here

This is because both dtc-lexer as well as dtc-parser define the same
global symbol yyloc. Before with -fcommon those were merged into one
defintion. The proper solution would be to mark this as "extern",
however that leads to:

  dtc-lexer.l:26:16: error: redundant redeclaration of 'yylloc' [-Werror=redundant-decls]
   26 | extern YYLTYPE yylloc;
      |                ^~~~~~
In file included from dtc-lexer.l:24:
dtc-parser.tab.h:127:16: note: previous declaration of 'yylloc' was here
  127 | extern YYLTYPE yylloc;
      |                ^~~~~~
cc1: all warnings being treated as errors

which means the declaration is completely redundant and can just be
dropped.

Signed-off-by: Dirk Mueller <dmueller@suse.com>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
[robh: cherry-pick from upstream]
Cc: stable@vger.kernel.org
Signed-off-by: Rob Herring <robh@kernel.org>
---
 scripts/dtc/dtc-lexer.l | 1 -
 1 file changed, 1 deletion(-)

diff --git a/scripts/dtc/dtc-lexer.l b/scripts/dtc/dtc-lexer.l
index 5c6c3fd557d7..b3b7270300de 100644
--- a/scripts/dtc/dtc-lexer.l
+++ b/scripts/dtc/dtc-lexer.l
@@ -23,7 +23,6 @@ LINECOMMENT	"//".*\n
 #include "srcpos.h"
 #include "dtc-parser.tab.h"
 
-YYLTYPE yylloc;
 extern bool treesource_error;
 
 /* CAUTION: this will stop working if we ever use yyless() or yyunput() */
diff --git a/scripts/dtc/dtc-lexer.lex.c_shipped b/scripts/dtc/dtc-lexer.lex.c_shipped
index 5c6c3fd557d7..b3b7270300de 100644
--- a/scripts/dtc/dtc-lexer.lex.c_shipped
+++ b/scripts/dtc/dtc-lexer.lex.c_shipped
@@ -23,7 +23,6 @@ LINECOMMENT	"//".*\n
 #include "srcpos.h"
 #include "dtc-parser.tab.h"
 
-YYLTYPE yylloc;
 extern bool treesource_error;
 
 /* CAUTION: this will stop working if we ever use yyless() or yyunput() */
EOF
    fi

    # See also commit 52a9dab6d892 ("libsubcmd: Fix use-after-free for
    # realloc(..., 0)") # v5.17
    if kernel_version_le 2.6.38 "$1" && kernel_version_lt "$1" 5.17; then
	patch -p1 -f -s <<'EOF'
From 52a9dab6d892763b2a8334a568bd4e2c1a6fde66 Mon Sep 17 00:00:00 2001
From: Kees Cook <keescook@chromium.org>
Date: Sun, 13 Feb 2022 10:24:43 -0800
Subject: [PATCH] libsubcmd: Fix use-after-free for realloc(..., 0)

GCC 12 correctly reports a potential use-after-free condition in the
xrealloc helper. Fix the warning by avoiding an implicit "free(ptr)"
when size == 0:

In file included from help.c:12:
In function 'xrealloc',
    inlined from 'add_cmdname' at help.c:24:2: subcmd-util.h:56:23: error: pointer may be used after 'realloc' [-Werror=use-after-free]
   56 |                 ret = realloc(ptr, size);
      |                       ^~~~~~~~~~~~~~~~~~
subcmd-util.h:52:21: note: call to 'realloc' here
   52 |         void *ret = realloc(ptr, size);
      |                     ^~~~~~~~~~~~~~~~~~
subcmd-util.h:58:31: error: pointer may be used after 'realloc' [-Werror=use-after-free]
   58 |                         ret = realloc(ptr, 1);
      |                               ^~~~~~~~~~~~~~~
subcmd-util.h:52:21: note: call to 'realloc' here
   52 |         void *ret = realloc(ptr, size);
      |                     ^~~~~~~~~~~~~~~~~~

Fixes: 2f4ce5ec1d447beb ("perf tools: Finalize subcmd independence")
Reported-by: Valdis Klētnieks <valdis.kletnieks@vt.edu>
Signed-off-by: Kees Kook <keescook@chromium.org>
Tested-by: Valdis Klētnieks <valdis.kletnieks@vt.edu>
Tested-by: Justin M. Forbes <jforbes@fedoraproject.org>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: linux-hardening@vger.kernel.org
Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
Link: http://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/lib/subcmd/subcmd-util.h | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h
index 794a375dad36..b2aec04fce8f 100644
--- a/tools/lib/subcmd/subcmd-util.h
+++ b/tools/lib/subcmd/subcmd-util.h
@@ -50,15 +50,8 @@ static NORETURN inline void die(const char *err, ...)
 static inline void *xrealloc(void *ptr, size_t size)
 {
 	void *ret = realloc(ptr, size);
-	if (!ret && !size)
-		ret = realloc(ptr, 1);
-	if (!ret) {
-		ret = realloc(ptr, size);
-		if (!ret && !size)
-			ret = realloc(ptr, 1);
-		if (!ret)
-			die("Out of memory, realloc failed");
-	}
+	if (!ret)
+		die("Out of memory, realloc failed");
 	return ret;
 }
EOF
    fi

    # Use sed to patch the ____ilog2_NaN() prototype.
    sed -i 's/__attribute__((const, noreturn))/__attribute__((noreturn))/' \
	include/linux/log2.h tools/include/linux/log2.h 2>/dev/null

    # After patch-v4.14.1[12] has been applied, the execute bit has to be
    # set for sync-check.sh since patch can't do that.
    for f in "tools/objtool/sync-check.sh"; do
      if [ -e "$f" ]; then
	chmod a+x "$f"
      fi
    done
}

function download_and_extract_distro_rpm {
    [ -n "$1" ] || return $?
    set -- ${1//^/ }
    local kver=$1
    local distro=$2
    local release=$3
    (
	cd "${kernel_downloads}" || exit $?
	read -a urls -r <<<"$(get_srpm_urls "$distro" "$release" x86_64 |
			      tr '\n' ' ')"
	for url in "${urls[@]}"; do
	    case "$distro" in
		CentOS|AlmaLinux)
		    wget -q -nc "${url}/kernel-${kver}.src.rpm" && break
		    ;;
		UEK)
		    wget -q -nc "${url}/kernel-uek-${kver}.src.rpm" && break
		    ;;
	    esac
	done
    )
    local tmpdir=kernel-tree-tmp-$$
    rm -rf "linux-$1" "${tmpdir}"
    mkdir "${tmpdir}" || return $?
    (
	cd "${tmpdir}" &&
	    case "$distro" in
		CentOS|AlmaLinux)
		    rpm2cpio "${kernel_downloads}/kernel-${kver}.src.rpm" |
			cpio -i --make-directories --quiet &&
			tar xaf "linux-${kver}"*.tar.* &&
			if [ -e "linux-${kver}" ]; then
			    mv "linux-${kver}" ..
			else
			    mv "linux-${kver}"*[^z] "../linux-${kver}"
			fi
		    ;;
		UEK)
		    rpm2cpio "${kernel_downloads}/kernel-uek-${kver}.src.rpm" |
			cpio -i --make-directories --quiet &&
			tar xaf "linux-${kver/-*}.tar."* &&
			mv "linux-${kver/-*}" "../linux-${kver}"
		    ;;
		*)
		    echo "Error: unknown distro $distro"
		    ;;
	    esac
    ) || return $?
    rm -rf "${tmpdir}"
}

function download_and_extract_kernel_tree {
    if [ "${1/^}" = "$1" ]; then
	# Upstream kernel.
	if [ -e "${kernel_tree}" ]; then
	    # If a git kernel tree is available, obtain the kernel source code
	    # from that git tree.
	    (
		cd "${kernel_tree}" &&
		    {
			{ git tag -l "v$1" | grep -q '^v'; } ||
			    {
				echo "Could not find tag v$1;" \
				     "updating git repository" 1>&2
				git fetch origin
				git fetch stable
			    }
		    } &&
		    { git tag -l "v$1" | grep -q '^v'; } &&
		    git archive "v$1"
	    ) | {
		rm -rf "linux-$1" &&
		    mkdir "linux-$1" &&
		    tar -C "linux-$1" -xf- 2>/dev/null
	    }
	else
	    # Otherwise download a source code tar file and extract it.
	    download_kernel "$1" && extract_kernel_tree "$1"
	fi
    else
	# Distro kernel.
	download_and_extract_distro_rpm "$1"
    fi &&
	(cd "linux-${1/^*}" && patch_kernel "${1/^*}")
}

# For shellcheck
if false; then
    quiet_download=true
fi
